package com.chinacreator.galaxylive.web.rest;

import com.chinacreator.c2.config.ConfigManager;
import com.chinacreator.c2.web.exception.InvalidRestParamException;
import com.chinacreator.c2.web.util.RestUtils;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLDecoder;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.chinacreator.galaxylive.bean.UserBean;
import com.chinacreator.galaxylive.service.PageService;
import com.chinacreator.galaxylive.utils.SessionUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpRequest;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.client.HttpClientErrorException;
import org.springframework.web.client.HttpServerErrorException;

/**
 * 简化用户前端调用服务网关的代理层
 *
 * @author Vurt
 */
@Controller
@RequestMapping("/proxy")
public class ProxyResource {

    private static final Logger LOGGER = LoggerFactory.getLogger(ProxyResource.class);

    @Autowired
    private HttpServletRequest request;
    @Autowired
    private PageService pageService;

    private static String serviceInterfaceUrl = ConfigManager.getInstance().getConfig("live.serviceInterfaceUrl");

    private static Pattern pattern = Pattern.compile("^(.*/proxy)(.*)$");

    private static HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();

    static {
        factory.setHttpClient(RestUtils.getHttpClient());
    }

    @RequestMapping("/**")
    public void proxy(HttpServletRequest request,HttpServletResponse response) throws IOException {
        URI uri;
        try {
            uri = resolveRemoteUri(request);
        } catch (Exception e) {
            response.sendError(HttpStatus.BAD_REQUEST.value(), e.getMessage());
            return;
        }
        // 2.发送请求
        ClientHttpRequest newRequest = factory.createRequest(uri, HttpMethod.valueOf(request.getMethod()));
        //clone请求头
        handleRequestHeaders(request, newRequest.getHeaders());
        //对接body
        InputStream requestBody;
        if((requestBody = request.getInputStream())!=null)
            IOUtils.copy(requestBody, newRequest.getBody());

        ClientHttpResponse remoteResponse;
        try {
            remoteResponse = newRequest.execute();
            response.setStatus(remoteResponse.getStatusCode().value());
            handleResponseHeaders(remoteResponse.getHeaders(), response);
            InputStream resultBody;
            if((resultBody=remoteResponse.getBody())!=null){
                IOUtils.copy(resultBody, response.getOutputStream());
            }
        } catch (Exception e) {
            if(e instanceof HttpClientErrorException){
                LOGGER.error("请求【"+uri+"】发生异常,异常信息为:"+((HttpClientErrorException)e).getResponseBodyAsString()) ;
            }else if(e instanceof HttpServerErrorException){
                LOGGER.error("请求【"+uri+"】发生异常,异常信息为:"+((HttpServerErrorException)e).getResponseBodyAsString()) ;
            }
            e.printStackTrace();
            response.sendError(HttpStatus.BAD_REQUEST.value(), e.getMessage());
        }
    }

    private URI resolveRemoteUri(HttpServletRequest request) throws Exception {
        // 1.截取远程请求uri
        String uriStr = getRemoteAPIUri(request);
        if (StringUtils.isEmpty(uriStr)) {
            LOGGER.error("请求:" + request.getRequestURI() + "截取远程地址失败.");
            throw new RuntimeException();
        }
        URI uri;
        try {
            String queryStr = request.getQueryString();
            if (StringUtils.isNotBlank(queryStr)) {
                queryStr = URLDecoder.decode(request.getQueryString(), "UTF-8");
            }
            URI temp =  new URI(serviceInterfaceUrl + uriStr);
            uri = new URI(temp.getScheme(),null,temp.getHost(),temp.getPort(),temp.getPath(),queryStr,null);
        } catch (URISyntaxException e) {
            e.printStackTrace();
            throw e;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
            throw e;
        }
        return uri;
    }

    private String getRemoteAPIUri(HttpServletRequest request) {
        String path = request.getRequestURI();
        Matcher matcher = pattern.matcher(path);
        if (matcher.find()) {
            return matcher.group(2);
        }
        return null;
    }

    private static Set<String> EXCLUDED_REQUEST_HEADERS = new HashSet<String>();
    private static Set<String> EXCLUDED_RESPONSE_HEADERS = new HashSet<String>();

    static {
        EXCLUDED_REQUEST_HEADERS.add("cookie");
        EXCLUDED_REQUEST_HEADERS.add("authorization");
        //TODO 现场调用data.json时只要带了host,就是404,非常诡异，为了不影响进度,先去掉
        EXCLUDED_REQUEST_HEADERS.add("host");

        EXCLUDED_RESPONSE_HEADERS.add("transfer-encoding");
        EXCLUDED_RESPONSE_HEADERS.add("set-cookie");
    }

    /**
     * 处理请求头，操作包括：复制原始请求头，移除不需要发送到后端的头，添加API-KEY相关头
     *
     * @param request 原始请求
     */
    private void handleRequestHeaders(HttpServletRequest request, HttpHeaders targetHeaders) {
        Enumeration<?> names = request.getHeaderNames();

        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            if (!EXCLUDED_REQUEST_HEADERS.contains(name.toLowerCase())) {
                targetHeaders.add(name, request.getHeader(name));
            }
        }
        UserBean userBean = SessionUtils.getSessionUser(request);
        if(userBean == null){
            userBean = pageService.getUserBean();
        }
        if(userBean != null){
            targetHeaders.set("galaxy_user_id_",userBean.getMemberId());
        }else {
            throw new InvalidRestParamException("获取用户信息失败");
        }

    }

    /**
     * 处理远程响应的头，排除掉一些不需要响应到前台的代理头
     */
    private void handleResponseHeaders(HttpHeaders remoteHeaders, HttpServletResponse response) {
        Set<Entry<String, List<String>>> entries = remoteHeaders.entrySet();

        for (Entry<String, List<String>> entry : entries) {
            String name = entry.getKey();
            if (!EXCLUDED_RESPONSE_HEADERS.contains(name.toLowerCase())) {
                for (String value : entry.getValue()) {
                    response.addHeader(name, value);
                }
            }
        }
    }
}
